#pragma once
#include <iostream>
#include <memory>
#include <vector>

#include "common.h"
#include "entity.h"
#include "vector2d.h"
#include "matrix.h"

class MapGrid;
class MapSystem {
 public:
  explicit MapSystem(RegionSystemType system_type, uint32_t height,
                     uint32_t width);
  ~MapSystem();

  void Init();

  void OnEntityIntoMap(EntityPtr entity);
  void OnEntityLeaveMap(EntityPtr entity);
  void OnEntityChangePos(EntityPtr entity, const Vector2D& old_pos,
                         const Vector2D& new_pos);

  inline uint32_t GetHeight() const { return height_; }
  inline uint32_t GetWidth() const { return width_; }

  bool IsInMap(const Vector2D& pos) const;

  std::string Print();

  uint32_t ToMapIndex(const Vector2D& pos) const;
  uint32_t ToMapIndex(uint32_t x_index, uint32_t y_index) const;
  bool MapIndexToPos(uint32_t index, Vector2D& pos) const;

  bool CheckStatus(const Vector2D& pos, EMapGridStatusType check_type) const;
  bool CheckStatus(uint32_t index, EMapGridStatusType check_type) const;
  bool CheckStatus(uint32_t x_index, uint32_t y_index,
                   EMapGridStatusType check_type) const;

  MapGrid* GetGrid(uint32_t grid_index);
  MapGrid* GetGrid(const Vector2D& pos);
  MapGrid* GetGrid(uint32_t x_index, uint32_t y_index);

  uint32_t GetGridIndex(uint32_t x_index, uint32_t y_index) const;
  uint32_t GetGridIndex(const Vector2D& pos) const;

  bool MapIndexToCenterPos(uint32_t x_index, uint32_t y_index,
                           Vector2D& pos) const;

  std::string ToString();
  bool LineInterGrid(const Vector2D& pos_0, const Vector2D& pos_1,
                     std::list<uint32_t>& grid_indexs);

  bool LineInterGridZ(ZNumber x0, ZNumber y0, ZNumber x1, ZNumber y1,
                      std::list<uint32_t>& grid_indexs);

  void AddInterGridZ(std::list<uint32_t>& grid_indexs, ZNumber x, ZNumber y,
                     bool steep, bool swap_start);

  bool IsRelationEntity(const Vector2D& start_pos, const Vector2D& target_pos,
                        const Vector2D& center_pos, const ZNumber& cl,
                        Vector2D& cvt);

  ECaculatePathResult CaculateMoveCriclePath(const Vector2D& start_pos,
                                             const Vector2D& target_pos,
                                             const Vector2D& center_pos,
                                             const ZNumber& cl,
                                             std::list<Vector2D>& paths);
  void MakePath(const Vector2D& pos_1, const Vector2D& pos_2,
                const Vector2D& center_pos, std::list<Vector2D>& paths);

  int64_t GetEntityId() { return ++g_entity_id;}
 private:
  RegionSystemType system_type_;
  std::vector<MapGrid> grids_;

  uint32_t min_grid_index_;
  uint32_t max_grid_index_;

  uint32_t height_;
  uint32_t width_;
  int64_t g_entity_id;
};
